package com.gitee.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.bo.CartAddBo;
import com.gitee.bo.CartUpdateBo;
import com.gitee.enums.ResponseEnum;
import com.gitee.exception.CartException;
import com.gitee.exception.FoodException;
import com.gitee.pojo.Cart;
import com.gitee.service.ICartService;
import com.gitee.service.ICookbookService;
import com.gitee.vo.CartItemVo;
import com.gitee.vo.CartVo;
import com.gitee.vo.CookbookVo;
import com.gitee.vo.FoodVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements ICartService {

    @Autowired
    private ICookbookService cookbookService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /** cart:userId:cookbookId:cookbookType */
    private static final String CART_KEY_TEMPLATE = "cart:%s:%s:%s";

    @Transactional
    @Override
    public Boolean addCart(Integer userId, CartAddBo cartAddBo) {
        //查找今天的菜谱，如果大于中午12点，则显示明天的菜谱
        CookbookVo cookbookVo = cookbookService.getTodayCookbook(cartAddBo.getCookbookType());
        // 判断菜谱中是否包含此菜品
        Optional<FoodVo> foodVoOptional = cookbookVo.getFoodVos().stream()
                .filter(foodVo -> foodVo.getId().equals(cartAddBo.getFoodId()))
                .findAny();
        if (!foodVoOptional.isPresent()) {
            throw new FoodException(ResponseEnum.NO_FOOD);
        }
        FoodVo foodVo = foodVoOptional.get();
        // 判断菜品库存
        if (foodVo.getStock() <= 0) {
            throw new FoodException(ResponseEnum.NO_STOCK);
        }
        //写入到Redis
        HashOperations<String, String, String> ho = redisTemplate.opsForHash();
        String cartKey = String.format(CART_KEY_TEMPLATE, userId, cookbookVo.getId(), cartAddBo.getCookbookType());
        String cartValue = ho.get(cartKey, String.valueOf(foodVo.getId()));
        Cart cart;
        //如果为空，就新增, 默认不选中
        if (StrUtil.isEmpty(cartValue)) {
            cart = new Cart(foodVo.getId(), 1, false);
        } else {
            //不为空，就修改数量+1
            cart = JSON.parseObject(cartValue, Cart.class);
            cart.setQuantity(cart.getQuantity() + 1);
        }
        ho.put(cartKey, String.valueOf(foodVo.getId()), JSON.toJSONString(cart));
        // 根据订餐截止日期设置过期时间
        return redisTemplate.expireAt(cartKey, cookbookVo.getEndTime());
    }

    @Override
    public CartVo updateCart(Integer userId, Integer foodId, Integer cookbookId, Integer cookbookType, CartUpdateBo cartUpdateBo) {
        HashOperations<String, String, String> ho = redisTemplate.opsForHash();
        String cartKey = String.format(CART_KEY_TEMPLATE, userId, cookbookId, cookbookType);
        String cartValue = ho.get(cartKey, String.valueOf(foodId));
        if (StrUtil.isEmpty(cartValue)) {
            throw new CartException(ResponseEnum.NO_FOOD);
        }
        Cart cart = JSONObject.parseObject(cartValue, Cart.class);
        if (cartUpdateBo.getQuantity() != null && cartUpdateBo.getQuantity() >= 0) {
            cart.setQuantity(cartUpdateBo.getQuantity());
        }
        if (cartUpdateBo.getSelected() != null) {
            cart.setSelected(cartUpdateBo.getSelected());
        }
        ho.put(cartKey, String.valueOf(foodId), JSON.toJSONString(cart));
        return getCart(userId, cookbookType);
    }

    @Override
    public CartVo removeCart(Integer userId, Integer foodId, Integer cookbookId, Integer cookbookType) {
        HashOperations<String, String, String> ho = redisTemplate.opsForHash();
        String cartKey = String.format(CART_KEY_TEMPLATE, userId, cookbookId, cookbookType);
        String cartValue = ho.get(cartKey, String.valueOf(foodId));
        if (StrUtil.isEmpty(cartValue)) {
            throw new CartException(ResponseEnum.NO_FOOD);
        }
        ho.delete(cartKey, String.valueOf(foodId));
        return getCart(userId, cookbookType);
    }

    @Override
    public CartVo getCart(Integer userId, Integer cookbookType) {
        CartVo cartVo = new CartVo();
        CookbookVo cookbookVo = cookbookService.getTodayCookbook(cookbookType);
        HashOperations<String, String, String> ho = redisTemplate.opsForHash();
        String cartKey = String.format(CART_KEY_TEMPLATE, userId, cookbookVo.getId(), cookbookType);
        // 验证购物车是否过期
        if (!redisTemplate.hasKey(cartKey)) {
            return cartVo;
        }
        // 查看购物车里的所有菜品
        Map<String, String> entries = ho.entries(cartKey);
        Set<String> foodIds = entries.keySet();
        if (CollectionUtil.isEmpty(foodIds)) {
            return cartVo;
        }
        Map<Integer, FoodVo> foodVoMap = cookbookVo.getFoodVos().stream().collect(Collectors.toMap(FoodVo::getId, foodVo -> foodVo));

        boolean selectedAll = true;
        BigDecimal totalPrice = BigDecimal.ZERO;
        Integer totalQuantity = 0;
        List<CartItemVo> cartItemVos = new ArrayList<>();
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Cart cart = JSON.parseObject(entry.getValue(), Cart.class);
            FoodVo foodVo = foodVoMap.get(cart.getFoodId());
            if (foodVo != null) {
                CartItemVo cartItemVo = buildCartItemVo(foodVo, cart);
                cartItemVos.add(cartItemVo);
                if (!cart.getSelected()) {
                    selectedAll = false;
                } else {
                    totalPrice = totalPrice.add(cartItemVo.getTotalPrice());
                    totalQuantity += cart.getQuantity();
                }
            }
        }
        cartVo.setCartItemVos(cartItemVos);
        //根据前端要求而定
        cartVo.setTotalPrice(totalPrice.multiply(BigDecimal.valueOf(100)));
        cartVo.setTotalQuantity(totalQuantity);
        cartVo.setSelectAll(selectedAll);
        cartVo.setCookbookId(cookbookVo.getId());
        return cartVo;
    }

    private CartItemVo buildCartItemVo(FoodVo foodVo, Cart cart) {
        CartItemVo cartItemVo = new CartItemVo();
        cartItemVo.setId(foodVo.getId());
        cartItemVo.setTitle(foodVo.getTitle());
        cartItemVo.setDescription(foodVo.getDescription());
        cartItemVo.setImg(foodVo.getImg());
        cartItemVo.setStock(foodVo.getStock());
        cartItemVo.setSelected(cart.getSelected());
        cartItemVo.setQuantity(cart.getQuantity());
        cartItemVo.setPrice(foodVo.getPrice());
        cartItemVo.setTotalPrice(foodVo.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity())));
        return cartItemVo;
    }

    @Override
    public CartVo settleCart(Integer userId, Integer cookbookType) {
        CartVo cartVo = getCart(userId, cookbookType);
        if (CollectionUtil.isEmpty(cartVo.getCartItemVos())) {
            throw new CartException(ResponseEnum.NO_CART);
        }
        List<CartItemVo> cartItemVos = cartVo.getCartItemVos().stream().filter(CartItemVo::getSelected).collect(Collectors.toList());
        cartVo.setCartItemVos(cartItemVos);
        return cartVo;
    }

    @Override
    public CartVo selectAll(Integer userId, Integer cookbookId, Integer cookbookType) {
        HashOperations<String, String, String> ho = redisTemplate.opsForHash();
        String cartKey = String.format(CART_KEY_TEMPLATE, userId, cookbookId, cookbookType);
        for (Cart cart : listCarts(userId, cookbookId, cookbookType)) {
            cart.setSelected(true);
            ho.put(cartKey, String.valueOf(cart.getFoodId()), JSON.toJSONString(cart));
        }
        return getCart(userId, cookbookType);
    }

    @Override
    public CartVo unSelectAll(Integer userId, Integer cookbookId, Integer cookbookType) {
        HashOperations<String, String, String> ho = redisTemplate.opsForHash();
        String cartKey = String.format(CART_KEY_TEMPLATE, userId, cookbookId, cookbookType);
        for (Cart cart : listCarts(userId, cookbookId, cookbookType)) {
            cart.setSelected(false);
            ho.put(cartKey, String.valueOf(cart.getFoodId()), JSON.toJSONString(cart));
        }
        return getCart(userId, cookbookType);
    }

    @Override
    public List<Cart> listCarts(Integer userId, Integer cookbookId, Integer cookbookType) {
        HashOperations<String, String, String> ho = redisTemplate.opsForHash();
        String cartKey = String.format(CART_KEY_TEMPLATE, userId, cookbookId, cookbookType);
        // 验证购物车是否过期
        if (!redisTemplate.hasKey(cartKey)) {
            throw new CartException(ResponseEnum.NO_CART);
        }
        Map<String, String> entries = ho.entries(cartKey);
        List<Cart> carts = new ArrayList<>();
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Cart cart = JSONObject.parseObject(entry.getValue(), Cart.class);
            carts.add(cart);
        }
        return carts;
    }
}
